package utils

import (
	"EinoService/utils/database"
	"database/sql"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"sync"
	"time"
)

var (
	// db singleton instance
	dbInstanceSet map[string]*gorm.DB
	// db singleton sync state
	dbOnceSyncSet map[string]*sync.Once
	// option config
	optionConfig DatabaseConfig
)

// initOption database config struct
func initOption(sequence string) *database.Options {
	var dbConfig *database.Options
	optionConfig = GetConfig().Database
	var config SubDatabaseConfig
	switch sequence {
	case "master":
		config = optionConfig.Master
		break
	case "slave":
		// todo
		panic(errors.New("还未配置slave"))
		break
	default:
		panic(errors.New(fmt.Sprintf("数据库序号：%v 未配置", sequence)))
	}
	dbConfig = &database.Options{
		Type:     config.Type,
		User:     config.User,
		Password: config.Passwd,
		Host:     config.Host,
		DbName:   config.Dbname,
		Port:     Ternary(config.Port > 0, config.Port, 3306),
		Prefix:   Ternary(config.Prefix != "", config.Prefix, ""),
		Debug:    config.Debug,
	}
	return dbConfig
}

// DbInstance instance db client
func DbInstance(dbConfigs ...string) *gorm.DB {
	configName := "master"
	if len(dbConfigs) > 0 && dbConfigs[0] != "" {
		configName = dbConfigs[0]
	}
	// init instance and sync
	if dbInstanceSet == nil {
		dbInstanceSet = make(map[string]*gorm.DB)
		dbOnceSyncSet = make(map[string]*sync.Once)
	}
	if client, ok := dbInstanceSet[configName]; ok {
		sqlDB, err := client.DB()
		if err == nil || sqlDB.Ping() == nil {
			return client
		}
	}

	dbOnceSyncSet[configName] = &sync.Once{}

	dbOnceSyncSet[configName].Do(func() {
		option := initOption(configName)
		var driver database.SqlClient
		switch option.Type {
		case "mysql":
			driver = &database.Mysql{}
			break
		//case "mssql":
		//	driver = &database.Mssql{}
		//	break
		//case "postgres":
		//	driver = &database.PostgresSQL{}
		//	break
		default:
			panic(fmt.Sprintf("database config's type:%s was not valid", option.Type))
		}
		Db := driver.Connect(option)
		if option.Debug {
			Db.Debug()
		}
		sqlDB, _ := Db.DB()
		sqlDB.SetMaxIdleConns(optionConfig.Pool.MaxIdleConnection)
		sqlDB.SetMaxOpenConns(optionConfig.Pool.MaxOpenConnection)
		sqlDB.SetConnMaxLifetime(time.Duration(optionConfig.Pool.MaxLifeTime) * time.Second)
		dbInstanceSet[configName] = Db
	})
	return dbInstanceSet[configName]
}

func DbClient(dbConfigs ...string) *sql.DB {
	configName := "master"
	if len(dbConfigs) > 0 && dbConfigs[0] != "" {
		configName = dbConfigs[0]
	}
	opt := initOption(configName)
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", opt.User, opt.Password, opt.Host, opt.Port, opt.DbName)
	Db, err := sql.Open(opt.Type, dsn)
	if err != nil {
		panic(err)
	}
	// 数据库设置
	Db.SetConnMaxLifetime(time.Minute * 10)
	Db.SetConnMaxIdleTime(time.Minute * 10)
	Db.SetMaxOpenConns(10)
	Db.SetMaxIdleConns(10)
	// 连接测试
	err = Db.Ping()
	if err != nil {
		fmt.Println("数据库连接失败")
		panic(err)
	}
	return Db
}
